
import redis, pymongo, sys, os, time

print(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

from exchange.conf.conf import conf
from exchange.utils.uuid import uuid

class db:

    # redis
    rdb = redis.Redis(host=conf.redis.host, port=conf.redis.port, db=conf.redis.db, decode_responses=True)

    # mongodb
    mdb = pymongo.MongoClient(host=conf.mongodb.host, port=conf.mongodb.port, connect=False)


    """
    create account
    """
    @staticmethod
    def create_account(username, password):
        db.mdb.exchange.account.insert_one({
            "username": username,
            "password": password,
            "userid": uuid.get_uuid(username + password)
        })


    """
    update account
    """
    @staticmethod
    def update_account(userid, account):
        db.mdb.exchange.account.update_one({"userid": userid}, {"$set": account})


    """
    查询账户信息
    """
    @staticmethod
    def get_account(userid):
        return db.mdb.exchange.account.find_one({ "userid": userid }, {"_id": 0})


    """
    查询余额
    """
    @staticmethod
    def get_balances(userid):
        data = [o for o in db.mdb.exchange.balances.find({"userid": userid}, {"_id": 0})]
        return data


    """
    查询提币地址
    """
    @staticmethod
    def get_address_by_withdraw(userid, coin):
        return db.mdb.exchange.coins.find_one({ "userid":userid, "coin": coin, "type": "withdraw" }, {"_id": 0})

    """
    设置提币地址
    """
    @staticmethod
    def set_address_by_withdraw( userid, coin, address, memo ):
        db.mdb.exchange.coins.update_one({"userid": userid, "coin": coin, "type": "withdraw"}, {"$set": {"address": address, "memo": memo}}, upsert=True)

    """
    查询充币地址
    """
    @staticmethod
    def get_address_by_recharge(userid, coin):
        return db.mdb.exchange.coins.find_one({ "userid":userid, "coin": coin, "type": "recharge" }, {"_id": 0})

    """
    查询交易对列表
    """
    @staticmethod
    def get_symbols():
        return set([a.split('.')[1] for a in db.mdb.exchange.list_collection_names() if 'orders.' in a])


    """
    查询 - 交易深度
    """
    @staticmethod
    def get_depth(symbol, depth=10):
        data = {}
        for action in ["buy", "sell"]:
            buf = db.rdb.get("depth.%s.%s" % (symbol, action))
            buf = [[[x, y] for x, y in o.split(",")] for o in buf.split("|")]
            if depth > 0:
                data[action] = buf[:depth]
        return data


    """
    更新 - 交易深度
    data:
        {"buy": [[1, 2], [3, 4]], "sell": [[1, 2], [3, 4]]}
    result:
        depth.eth/usdt.buy  = "1.2:200|1.1:150|1.09:440"
        depth.eth/usdt.sell = "1.3:400|1.4:350|1.51:240"
    """
    @staticmethod
    def set_depth(symbol, data, depth=10):
        for action in ["buy", "sell"]:
            if action in data:
                buf = '|'.join(["%s:%s" % (price, amount) for price, amount in data[action][:depth]])
                db.rdb.set("depth.%s.%s" % (symbol, action), buf)


    """
    查询账户信息 - 用户名
    """
    @staticmethod
    def get_account_by_username(username):
        return db.mdb.exchange.account.find_one({ "username": username }, {"_id": 0})

    """
    查询所有订单
    """
    @staticmethod
    def get_orders(symbol, userid=None, process_cancel=False):
        if process_cancel:
            db.process_orders_for_cancel(symbol, userid)
        if userid:
            return [a for a in db.mdb.exchange["orders." + symbol].find({ "account" : userid }, {"_id": 0})]
        else:
            return [a for a in db.mdb.exchange["orders." + symbol].find({}, {"_id": 0}).sort("time", 1)]

    """
    查询成交记录
    """
    @staticmethod
    def get_history(symbol, userid, limit=30):
        q = { "$or" : [ {"buyer": userid}, {"seller": userid} ] }
        c = { "_id": 0, "buy_seqno": 0, "sell_seqno": 0 }
        if limit > 0:
            return db.mdb.exchange["history." + symbol].find(q, c).sort("time", -1).limit(limit)
        else:
            return db.mdb.exchange["history." + symbol].find(q, c).sort("time", -1)

    """
    处理撤单逻辑
    """
    @staticmethod
    def process_orders_for_cancel(symbol, userid):
        for o in db.get_orders(symbol, userid):
            opened = o["amount"]
            for x in db.mdb.exchange["history." + symbol].find({o["action"] + "_seqno": o["seqno"], "status": 0}):
                opened -= x["amount"]
                # 用户撤单后，恢复用户的余额
                coin_sell, coin_buy = symbol.split("/")
                ca = db.mdb.exchange.account
                ch = db.mdb.exchange["history." + symbol]
                co = db.mdb.exchange["orders." + symbol]
                session = db.mdb.start_session()
                session.start_transaction()
                try:
                    if o["action"] == "buy" and not x["seller"]:
                        ca.update_one({"userid": x["buyer"]}, {"$inc": {"coins." + coin_buy + ".balances": x["amount"]}})
                        ch.update_one({"_id": x["_id"]}, {"$set": {"status": 1}})
                    if o["action"] == "sell" and not x["buyer"]:
                        ca.update_one({"userid": x["seller"]}, {"$inc": {"coins." + coin_sell + ".balances": x["amount"]}})
                        ch.update_one({"_id": x["_id"]}, {"$set": {"status": 1}})
                    if o["opened"] != opened:
                        o["opened"] = opened
                        if o["opened"] == 0.0:
                            co.delete_one({"seqno": o["seqno"]})
                        else:
                            co.update_one({"seqno": o["seqno"]}, {"$set": {"opened": o["opened"]}})
                except:
                    session.abort_transaction()
                else:
                    session.commit_transaction()
                finally:
                    session.end_session()


    """
    添加 queue cancels 记录
    """
    @staticmethod
    def add_cancels(account, symbol, action, seqno, price, amount):
        o = {
            "time": time.strftime("%Y-%m-%d %H:%M:%S"),
            "account": account,
            "seqno": seqno,
            "price": float(price),
            "amount": float(amount)
        }
        db.mdb.exchange["cancels.%s.%s" % (symbol, action)].insert_one(o)

    """
    查询历史撤单
    """
    @staticmethod
    def get_cancel(account, symbol, action, seqno):
        o = db.mdb.exchange["cancels.%s.%s" % (symbol, action)].find_one({"seqno": seqno})
        if o:
            del o["_id"]
            if o["account"] == account:
                return o
        return None

    """
    添加 queue orders 记录
    """
    @staticmethod
    def add_orders(account, symbol, action, seqno, price, amount):
        o = {
            "time": time.strftime("%Y-%m-%d %H:%M:%S"),
            "account": account,
            "seqno": seqno,
            "price": float(price),
            "amount": float(amount),
            "finished": 0
        }
        db.mdb.exchange["orders.%s.%s" % (symbol, action)].insert_one(o)

    """
    完成订单 用于快速 reload
    """
    @staticmethod
    def set_order_finished(symbol, action, seqno):
        result = db.mdb.exchange["orders.%s.%s" % (symbol, action)].update_one({"seqno": seqno}, {"$set": {"finished": 1}})
        return result

    """
    查询未完成的订单
    indexs : {"finished": 1}
    """
    @staticmethod
    def get_orders_by_reload(symbol, action):
        data = [o for o in db.mdb.exchange["orders.%s.%s" % (symbol, action)].find({"finished": 0}, {"_id": 0})]
        return data

    """
    查询历史订单
    """
    @staticmethod
    def get_order(account, symbol, action, seqno):
        o = db.mdb.exchange["orders.%s.%s" % (symbol, action)].find_one({"seqno": seqno})
        if o:
            del o["_id"]
            if o["account"] == account:
                return o
        return None

    """
    添加历史记录 - 成交
    """
    @staticmethod
    def add_history(symbol, action, buyer, seller, buy_seqno, sell_seqno, price, amount):
        db.mdb.exchange["history." + symbol].insert_one({
            "time": time.strftime("%Y-%m-%d %H:%M:%S"),
            "action": action,
            "buyer": buyer,
            "seller": seller,
            "buy_seqno": buy_seqno,
            "sell_seqno": sell_seqno,
            "price": float(price),
            "amount": float(amount)
        })

    """
    查询历史交易记录
    """
    @staticmethod
    def get_historys(symbol, action, seqno):
        data = [o for o in db.mdb.exchange["history.%s" % symbol].find({"%s_seqno" % action: seqno}, {"_id": 0})]
        return data


    """
    提币
    """
    @staticmethod
    def withdraw(account, coin, amount, address, memo):
        seqno = uuid.get_uuid(account + "coin.withdraw")
        x = {
            "time": time.strftime("%Y-%m-%d %H:%M:%S"),
            "seqno": seqno,
            "userid": account,
            "coin": coin,
            "address": address,
            "memo": memo,
            "amount": float(amount),
            "status": 0
        }
        cw = db.mdb.exchange.withdraw
        ca = db.mdb.exchange.account
        session = db.mdb.start_session()
        session.start_transaction()
        try:
            cw.insert_one(x)
            ca.update_one({"userid": account}, {"$inc": {"coins." + coin + ".balances": -float(amount)}})
        except:
            session.abort_transaction()
        else:
            session.commit_transaction()
            db.rdb.lpush("coin.withdraw.request", "%s,%s,%s,%s,%f" % (seqno, coin, address, memo, float(amount)))
        finally:
            session.end_session()

    """
    充币
    """

if __name__ == '__main__':

    # # test user orders
    # data = db.get_orders("swtc/cnyt", userid="977b578417ea4c243fc7bbcb61de7bb2", process_cancel=True)
    #
    # for o in data:
    #     print(o)


    print(db.get_symbols())
